Conversation
This stack of pull requests is managed by Graphite. Learn more about stacking. |
Prompt To Fix All With AIThis is a comment left during a code review.
Path: packages/enricher/src/enricher.test.ts
Line: 402-499
Comment:
**Prefer parameterised tests**
The five new `toComments` tests share the same three-step skeleton — parse a snippet, call `mockApiResponses`, assert on `toComments()` output — and differ only in the flag configuration and expected substrings. Per the team's convention they should be a single `test.each` table, e.g.:
```ts
test.each([
{
name: "rich line with url, active, rollout, evals",
flagOpts: { id: 42, filters: { groups: [{ rollout_percentage: 60, properties: [] }] } },
flagKey: "my-flag",
evalStats: [["my-flag", 1240, 230]] as [string, number, number][],
contains: ["active", "60% rolled out", "1,240 evals / 230 users (7d)", "feature_flags/42"],
notContains: [],
},
// … inactive, ghost, quiet-flag variants …
])("toComments $name", async ({ flagKey, flagOpts, evalStats, contains, notContains }) => {
const result = await enricher.parse(`posthog.getFeatureFlag('${flagKey}');`, "javascript");
mockApiResponses({ flags: [makeFlag(flagKey, flagOpts)], flagEvalStats: evalStats });
const annotated = (await result.enrichFromApi(API_CONFIG)).toComments();
contains.forEach((s) => expect(annotated).toContain(s));
notContains.forEach((s) => expect(annotated).not.toContain(s));
});
```
This reduces repetition without losing coverage.
**Context Used:** Do not attempt to comment on incorrect alphabetica... ([source](https://app.greptile.com/review/custom-context?memory=instruction-0))
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: packages/enricher/src/comment-formatter.ts
Line: 20-22
Comment:
**"inactive" appears twice for the same flag**
When a flag has `active: false`, the comment will contain both the explicit `"inactive"` segment (line 21) and `"STALE (inactive)"` from the staleness classifier — confirmed by the test that asserts both are present. The staleness segment already communicates the reason the flag is inactive, so displaying `"inactive"` as a separate part is redundant and violates the OnceAndOnlyOnce simplicity rule.
Consider either suppressing the `active`/`inactive` label when `flag.staleness === "inactive"`, or only showing the active/inactive label and dropping it from the staleness display.
**Context Used:** Do not attempt to comment on incorrect alphabetica... ([source](https://app.greptile.com/review/custom-context?memory=instruction-0))
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "feat: improve flag enricher" | Re-trigger Greptile |
| flagEvalStats: [["my-flag", 1240, 230]], | ||
| }); | ||
| const enriched = await result.enrichFromApi(API_CONFIG); | ||
|
|
||
| expect(enriched.flags[0].url).toBe( | ||
| "https://test.posthog.com/project/1/feature_flags/42", | ||
| ); | ||
| expect(enriched.flags[0].evaluationStats).toEqual({ | ||
| evaluations: 1240, | ||
| uniqueUsers: 230, | ||
| }); | ||
| }); | ||
|
|
||
| test("toComments renders rich flag line with url, active, rollout, evals", async () => { | ||
| const code = `posthog.getFeatureFlag('my-flag');`; | ||
| const result = await enricher.parse(code, "javascript"); | ||
|
|
||
| mockApiResponses({ | ||
| flags: [ | ||
| makeFlag("my-flag", { | ||
| id: 42, | ||
| filters: { groups: [{ rollout_percentage: 60, properties: [] }] }, | ||
| }), | ||
| ], | ||
| flagEvalStats: [["my-flag", 1240, 230]], | ||
| }); | ||
| const enriched = await result.enrichFromApi(API_CONFIG); | ||
|
|
||
| const annotated = enriched.toComments(); | ||
| expect(annotated).toContain(`Flag: "my-flag"`); | ||
| expect(annotated).toContain("active"); | ||
| expect(annotated).toContain("60% rolled out"); | ||
| expect(annotated).toContain("1,240 evals / 230 users (7d)"); | ||
| expect(annotated).toContain( | ||
| "https://test.posthog.com/project/1/feature_flags/42", | ||
| ); | ||
| }); | ||
|
|
||
| test("toComments marks inactive flags explicitly", async () => { | ||
| const code = `posthog.getFeatureFlag('off-flag');`; | ||
| const result = await enricher.parse(code, "javascript"); | ||
|
|
||
| mockApiResponses({ flags: [makeFlag("off-flag", { active: false })] }); | ||
| const enriched = await result.enrichFromApi(API_CONFIG); | ||
|
|
||
| const annotated = enriched.toComments(); | ||
| expect(annotated).toContain("inactive"); | ||
| expect(annotated).toContain("STALE (inactive)"); | ||
| }); | ||
|
|
||
| test("toComments handles flag not in PostHog", async () => { | ||
| const code = `posthog.getFeatureFlag('ghost-flag');`; | ||
| const result = await enricher.parse(code, "javascript"); | ||
|
|
||
| mockApiResponses({ flags: [] }); | ||
| const enriched = await result.enrichFromApi(API_CONFIG); | ||
|
|
||
| const annotated = enriched.toComments(); | ||
| expect(annotated).toContain(`Flag: "ghost-flag" \u2014 not in PostHog`); | ||
| }); | ||
|
|
||
| test("toComments omits evaluation segment when stats missing", async () => { | ||
| const code = `posthog.getFeatureFlag('quiet-flag');`; | ||
| const result = await enricher.parse(code, "javascript"); | ||
|
|
||
| mockApiResponses({ flags: [makeFlag("quiet-flag", { id: 7 })] }); | ||
| const enriched = await result.enrichFromApi(API_CONFIG); | ||
|
|
||
| const annotated = enriched.toComments(); | ||
| expect(annotated).toContain(`Flag: "quiet-flag"`); | ||
| expect(annotated).not.toContain("evals /"); | ||
| expect(annotated).toContain( | ||
| "https://test.posthog.com/project/1/feature_flags/7", | ||
| ); | ||
| }); | ||
|
|
||
| test("getFlagEvaluationStats is called with detected flag keys", async () => { | ||
| const code = `posthog.getFeatureFlag('tracked-flag');`; | ||
| const result = await enricher.parse(code, "javascript"); | ||
|
|
||
| mockApiResponses({ | ||
| flags: [makeFlag("tracked-flag")], | ||
| flagEvalStats: [["tracked-flag", 10, 5]], | ||
| }); | ||
| await result.enrichFromApi(API_CONFIG); | ||
|
|
||
| const calls = vi.mocked(fetch).mock.calls; | ||
| const queryPost = calls.find( | ||
| ([url, init]) => | ||
| String(url).includes("/query/") && init?.method === "POST", | ||
| ); | ||
| expect(queryPost).toBeDefined(); | ||
| const body = String(queryPost?.[1]?.body ?? ""); | ||
| expect(body).toContain("$feature_flag_called"); | ||
| expect(body).toContain("tracked-flag"); | ||
| }); | ||
|
|
||
| test("enrichFromApi with no detected usage returns empty enrichment", async () => { |
There was a problem hiding this comment.
The five new toComments tests share the same three-step skeleton — parse a snippet, call mockApiResponses, assert on toComments() output — and differ only in the flag configuration and expected substrings. Per the team's convention they should be a single test.each table, e.g.:
test.each([
{
name: "rich line with url, active, rollout, evals",
flagOpts: { id: 42, filters: { groups: [{ rollout_percentage: 60, properties: [] }] } },
flagKey: "my-flag",
evalStats: [["my-flag", 1240, 230]] as [string, number, number][],
contains: ["active", "60% rolled out", "1,240 evals / 230 users (7d)", "feature_flags/42"],
notContains: [],
},
// … inactive, ghost, quiet-flag variants …
])("toComments $name", async ({ flagKey, flagOpts, evalStats, contains, notContains }) => {
const result = await enricher.parse(`posthog.getFeatureFlag('${flagKey}');`, "javascript");
mockApiResponses({ flags: [makeFlag(flagKey, flagOpts)], flagEvalStats: evalStats });
const annotated = (await result.enrichFromApi(API_CONFIG)).toComments();
contains.forEach((s) => expect(annotated).toContain(s));
notContains.forEach((s) => expect(annotated).not.toContain(s));
});This reduces repetition without losing coverage.
Context Used: Do not attempt to comment on incorrect alphabetica... (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/enricher/src/enricher.test.ts
Line: 402-499
Comment:
**Prefer parameterised tests**
The five new `toComments` tests share the same three-step skeleton — parse a snippet, call `mockApiResponses`, assert on `toComments()` output — and differ only in the flag configuration and expected substrings. Per the team's convention they should be a single `test.each` table, e.g.:
```ts
test.each([
{
name: "rich line with url, active, rollout, evals",
flagOpts: { id: 42, filters: { groups: [{ rollout_percentage: 60, properties: [] }] } },
flagKey: "my-flag",
evalStats: [["my-flag", 1240, 230]] as [string, number, number][],
contains: ["active", "60% rolled out", "1,240 evals / 230 users (7d)", "feature_flags/42"],
notContains: [],
},
// … inactive, ghost, quiet-flag variants …
])("toComments $name", async ({ flagKey, flagOpts, evalStats, contains, notContains }) => {
const result = await enricher.parse(`posthog.getFeatureFlag('${flagKey}');`, "javascript");
mockApiResponses({ flags: [makeFlag(flagKey, flagOpts)], flagEvalStats: evalStats });
const annotated = (await result.enrichFromApi(API_CONFIG)).toComments();
contains.forEach((s) => expect(annotated).toContain(s));
notContains.forEach((s) => expect(annotated).not.toContain(s));
});
```
This reduces repetition without losing coverage.
**Context Used:** Do not attempt to comment on incorrect alphabetica... ([source](https://app.greptile.com/review/custom-context?memory=instruction-0))
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| if (flag.rollout !== null) { | ||
| parts.push(`${flag.rollout}% rolled out`); | ||
| } |
There was a problem hiding this comment.
"inactive" appears twice for the same flag
When a flag has active: false, the comment will contain both the explicit "inactive" segment (line 21) and "STALE (inactive)" from the staleness classifier — confirmed by the test that asserts both are present. The staleness segment already communicates the reason the flag is inactive, so displaying "inactive" as a separate part is redundant and violates the OnceAndOnlyOnce simplicity rule.
Consider either suppressing the active/inactive label when flag.staleness === "inactive", or only showing the active/inactive label and dropping it from the staleness display.
Context Used: Do not attempt to comment on incorrect alphabetica... (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/enricher/src/comment-formatter.ts
Line: 20-22
Comment:
**"inactive" appears twice for the same flag**
When a flag has `active: false`, the comment will contain both the explicit `"inactive"` segment (line 21) and `"STALE (inactive)"` from the staleness classifier — confirmed by the test that asserts both are present. The staleness segment already communicates the reason the flag is inactive, so displaying `"inactive"` as a separate part is redundant and violates the OnceAndOnlyOnce simplicity rule.
Consider either suppressing the `active`/`inactive` label when `flag.staleness === "inactive"`, or only showing the active/inactive label and dropping it from the staleness display.
**Context Used:** Do not attempt to comment on incorrect alphabetica... ([source](https://app.greptile.com/review/custom-context?memory=instruction-0))
How can I resolve this? If you propose a fix, please make it concise.
TLDR
Flag comments now include active/inactive status, 7-day evaluation stats (evals and unique users), and a direct link to the flag in PostHog.
Problem
Flag annotations in code comments lacked context about whether a flag was actually active, how much traffic it was seeing, and where to find it in PostHog. Developers had to navigate to PostHog manually to get this information.
Changes
getFlagEvaluationStatstoPostHogApi, which queries HogQL for$feature_flag_calledevents over a configurable lookback window (defaulting to 7 days) and returns per-flag evaluation counts and unique user countsbuildFlagUrlhelper that constructs a direct PostHog URL to a feature flag given the host, project ID, and flag IDactive/inactivestatus derived from the flag'sactivefieldN evals / M users (7d)when availablenot in PostHogwhen the flag key has no matching flagEnrichedFlag,EnrichedListItem, andEnrichmentContexttypes extended to carryurl,evaluationStats,active,host, andprojectIdParseResult.enrichFromApinow fetches flag evaluation stats in parallel with other API calls and passeshostandprojectIdthrough the enrichment context